<?php

namespace yunj\core\traits;

use think\Collection;
use think\db\BaseQuery;
use think\db\Query;
use think\db\Raw;
use think\facade\Db;
use yunj\core\Model;

/**
 * 数据分表
 * Trait ModelSplit
 * @package yunj\core\traits
 */
trait ModelSplit {

    use ModelHelper;

    /**
     * 获取当前年份后缀的模型
     * @return static
     */
    public function getCurrYearModel() {
        return $this->getSplitModel(date('Y'));
    }

    /**
     * 获取当前月份后缀的模型
     * @return static
     */
    public function getCurrMonthModel() {
        return $this->getSplitModel(date('Ym'));
    }

    /**
     * 获取当前天后缀的模型
     * @return static
     */
    public function getCurrDayModel() {
        return $this->getSplitModel(date('Ymd'));
    }

    /**
     * 获取指定后缀数据表的模型（数据表不存在则新增数据表后返回）
     * @param string $tableSuffix
     * @return static|null
     */
    public function getSplitModel(string $tableSuffix) {
        if (!$tableSuffix) return null;
        return $this->setCurrSplitTable($tableSuffix);
    }

    /**
     * 获取指定表后缀分表模型
     * @param string $tableSuffix
     * @return static|null
     */
    public function getExistSplitModel(string $tableSuffix) {
        if (!$tableSuffix) return null;
        $refTable = $this->getRefTable();           // 参考表名
        /** @var Model $this */
        $table = $refTable . "_" . $tableSuffix;    // 当前分表名
        if (!db_table_exist($table)) return null;
        $this->table = $table;
        return $this;
    }

    /**
     * 获取参考表名
     * @return mixed
     */
    public function getRefTable() {
        // 重新生成实例，防止反复加后缀的情况
        return $this->newInstance()->getTable();
    }

    /**
     * 设置当前分表，并返回当前模型
     * @param string $tableSuffix 分表后缀
     * @return static
     */
    private function setCurrSplitTable(string $tableSuffix) {
        $refTable = $this->getRefTable();               // 参考表名
        $table = $refTable . "_" . $tableSuffix;        // 当前分表名

        // 判断分表是否存在，不存在则新增
        if (!db_table_exist($table)) {
            Db::execute("create table {$table} like {$refTable}");
        }
        // 设置当前表名
        /** @var Model $this */
        $this->table = $table;
        return $this;
    }

    /**
     * 获取多个后缀的表联查query
     * @param array $tableSuffixArr 数据表后缀数组，如:['202301','202302'] 表示查询后缀为_202301和_202302这两张数据表数据
     * @param callable|null $queryHandle 单张表的查询query处理
     * @return Query|mixed
     */
    public function getUnionAllQuery(array $tableSuffixArr, ?callable $queryHandle = null) {
        $refTable = $this->getRefTable();               // 参考表名
        /** @var Model $this */
        $dbName = $this->getConfig('database');
        $allTables = Db::query("select table_name as `table_name` from information_schema.TABLES where table_schema = '{$dbName}' and table_name like '{$refTable}_%'");
        $allTables = json_decode(json_encode($allTables, JSON_UNESCAPED_UNICODE), true);
        $allTables = array_column($allTables, 'table_name');
        $unionQueryStr = '';
        foreach ($tableSuffixArr as $suffix) {
            $table = $refTable . '_' . $suffix;
            if (in_array($table, $allTables)) {
                $query = Db::table($table);
                $queryHandle && $queryHandle($query);
                $sql = $query->buildSql();
                $unionQueryStr .= ($unionQueryStr ? ' union all ' : '') . $sql;
            }
        }
        if (!$unionQueryStr) {
            return null;
        }
        $unionQuery = Db::table("({$unionQueryStr}) tmp");
        return $unionQuery;
    }

    /**
     * 获取多数据表数据条数
     * @param array $tableSuffixArr 数据表后缀数组，如:['202301','202302'] 表示查询后缀为_202301和_202302这两张数据表数据
     * @param array|mixed $where
     * @return int
     */
    public function getUnionAllCount($tableSuffixArr, $where = []) {
        $unionQuery = $this->getUnionAllQuery($tableSuffixArr, function ($query) use ($where) {
            $query = $this->buildWhere($query, $where);
        });
        if (!$unionQuery) {
            return 0;
        }
        return $unionQuery->count();
    }

    /**
     * 获取多数据表多行数据
     * @param array $tableSuffixArr 数据表后缀数组，如:['202301','202302'] 表示查询后缀为_202301和_202302这两张数据表数据
     * @param array $where
     * @param string|string[] $field
     * @param string|array|Raw $order
     * @param int $page 分页
     * @param int $pageSize 分页数据量
     * @return Collection|array
     */
    public function getUnionAllRows($tableSuffixArr, array $where = [], $field = ["*"], $order = [], int $page = 0, int $pageSize = 0) {
        $unionQuery = $this->getUnionAllQuery($tableSuffixArr, function ($query) use ($where, $field, $order) {
            $query = $this->buildWhere($query, $where);
            $query->field($field);
        });
        if (!$unionQuery) {
            return collect([]);
        }
        $unionQuery->order($order);
        if ($page && $pageSize) {
            $unionQuery->limit(($page - 1) * $pageSize, $pageSize);
        }
        return $unionQuery->select();
    }

    /**
     * 获取多数据表多行数据，并转换为数组
     * @param array $tableSuffixArr 数据表后缀数组，如:['202301','202302'] 表示查询后缀为_202301和_202302这两张数据表数据
     * @param array $where
     * @param string|string[] $field
     * @param string|array|Raw $order
     * @param int $page 分页
     * @param int $pageSize 分页数据量
     * @return array
     */
    public function getUnionAllRowsToArray($tableSuffixArr, array $where = [], $field = ["*"], $order = [], int $page = 0, int $pageSize = 0): array {
        $res = $this->getUnionAllRows($tableSuffixArr, $where, $field, $order, $page, $pageSize);
        return $res ? $res->toArray() : [];
    }

}